/* eslint-disable no-mixed-operators */
/* eslint-disable no-bitwise */
//
// QSimulator.js：量子电路测量计算所用的类
//

// import { init } from 'echarts';
// import { toHandlers } from 'vue';
// eslint-disable-next-line import/extensions
import Complex from './Complex.js';

// var name = new Array("X","Y","Z","H","S","T","RX","RY","RZ","M","CX","CY","CZ");

const I = -1;
const X = 0;
const Y = 1;
const Z = 2;
const H = 3;
const S = 4;
const M = 9;
const CX = 10;

class Qubit {
  constructor(n_ = 2) {
    this.n = n_;
    this.v = new Array(n_);
    this.init();
  }

  init() {
    const that = this;
    for (let i = 0; i < that.n; ++i) { that.v[i] = new Complex(0); }
    that.v[0] = new Complex(1);
  }

  getAmplitude() {
    const that = this;
    console.log('Qubit: ');
    for (let i = 0; i < that.n; ++i) { console.log(that.v[i].mod2()); }
  }
}

// class Qreg {
//   constructor(n_ = 4) {
//     this.n = n_;
//     this.reg = new Array(n_);
//     this.init();
//   }

//   init() {
//     const that = this;
//     for (let i = 0; i < that.n; ++i) { that.reg[i] = new Qubit(); }
//   }
// }

class Gate {
  constructor(r = 2, c = 2, type = I) {
    this.ro = r;
    this.co = c;
    this.type = type;
    this.allocate();
    this.init();
  }

  allocate() {
    const that = this;
    that.m = new Array(that.ro);
    for (let r = 0; r < that.ro; ++r) {
      that.m[r] = new Array(that.co);
      for (let c = 0; c < that.co; ++c) {
        if (r == c) { that.m[r][c] = new Complex(1); } else { that.m[r][c] = new Complex(0); }
      }
    }
  }

  init() {
    const that = this;
    switch (that.type) {
      case I: case CX:
        break;
      case X:
        that.m[0][0] = new Complex(0);
        that.m[0][1] = new Complex(1);
        that.m[1][0] = new Complex(1);
        that.m[1][1] = new Complex(0);
        break;
      case Y:
        that.m[0][0] = new Complex(0);
        that.m[0][1] = new Complex(0, -1);
        that.m[1][0] = new Complex(0, 1);
        that.m[1][1] = new Complex(0);
        break;
      case Z:
        that.m[0][0] = new Complex(1);
        that.m[0][1] = new Complex(0);
        that.m[1][0] = new Complex(0);
        that.m[1][1] = new Complex(-1);
        break;
      case H:
        that.m[0][0] = new Complex(1.0 / Math.sqrt(2));
        that.m[0][1] = new Complex(1.0 / Math.sqrt(2));
        that.m[1][0] = new Complex(1.0 / Math.sqrt(2));
        that.m[1][1] = new Complex(-1.0 / Math.sqrt(2));
        break;
      case S:
        that.m[0][0] = new Complex(1);
        that.m[0][1] = new Complex(0);
        that.m[1][0] = new Complex(0);
        that.m[1][1] = new Complex(0, 1);
        break;
      default:
        break;
    }
  }

  getGate() {
    console.log('+++getGate+++');
    for (let r = 0; r < this.ro; ++r) {
      for (let c = 0; c < this.co; ++c) { console.log(this.m[r][c]); }
    }
    console.log('===getGate===');
  }
}

// 张量积
// function QubitTensorProduct(a, b) {
//   const q = new Qubit(a.n * b.n);
//   for (let i = 0; i < a.n; ++i) {
//     for (let j = 0; j < b.n; ++j) { q.v[i * b.n + j] = a.v[i].mul(b.v[j]); }
//   }
//   return q;
// }

function GateTensorProduct(a, b) {
  const g = new Gate(a.ro * b.ro, a.co * b.co);
  for (let i = 0; i < g.ro; ++i) {
    for (let j = 0; j < g.co; ++j) {
      g.m[i][j] = a.m[parseInt(i / b.ro, 10)][parseInt(j / b.co, 10)].mul(b.m[i % b.ro][j % b.co]);
    }
  }
  return g;
}

// 线性变换
// Gate, Qubit
function linearTransform(g, b) {
  const q = new Qubit(g.ro);
  for (let i = 0; i < g.ro; ++i) {
    q.v[i] = new Complex(0);
    for (let j = 0; j < g.co; ++j) {
      q.v[i] = q.v[i].add(g.m[i][j].mul(b.v[j]));
    }
  }
  return q;
}

class QSimulator {
  constructor(maxGate) {
    this.maxGate = maxGate;
  }

  init(nline, circuit, obj) {
    const that = this;
    that.nq = nline;
    // that.nGate = nGate;
    that.circuit = circuit;
    that.obj = obj;
    // 初始化 Qubit 状态
    // that.qr = new Qreg(that.nq);
    that.state = new Qubit(1 << that.nq);
    // 初始化 Gate
    // that.buildGate();
    that.nmeasure = 0;
    that.ismeasure = new Array(that.nq);
    for (let i = 0; i < that.nq; ++i) { that.ismeasure[i] = 0; }
    that.measure();
  }

  buildSingleGate(idx, type) {
    const that = this;
    const IGate = new Gate(2, 2, I);
    const myGate = new Gate(2, 2, type);
    let g = new Gate(2, 2, I);
    if (idx == 0) { g = new Gate(2, 2, type); }
    for (let i = 1; i < that.nq; ++i) {
      if (i == idx) { g = GateTensorProduct(g, myGate); } else { g = GateTensorProduct(g, IGate); }
    }
    return g;
  }

  singleGate(idx, type) {
    const that = this;
    const g = that.buildSingleGate(idx, type);
    that.state = linearTransform(g, that.state);
  }

  buildCXGate(CB, TB) {
    function isCtrl(num) {
      if ((num & CB) == CB && (num & TB) == 0) { return (num | TB); }
      return -1;
    }
    const that = this;
    let ret; let
      tmp;
    const CXGate = new Gate((1 << that.nq), (1 << that.nq), CX); // IGate initialize
    for (let i = 0; i < (1 << that.nq); ++i) {
      ret = isCtrl(i);
      if (ret != -1) {
        tmp = CXGate.m[i];
        CXGate.m[i] = CXGate.m[ret];
        CXGate.m[ret] = tmp;
      }
    }
    return CXGate;
  }

  cx(idxc, idxt) {
    const that = this;
    const CB = (1 << (that.nq - 1) >> idxc); // control bit
    const TB = (1 << (that.nq - 1) >> idxt); // target bit
    const g = that.buildCXGate(CB, TB);
    console.log('CXGate: ', g);
    that.state = linearTransform(g, that.state);
  }

  measure() {
    const that = this;
    // 逐列检查
    for (let j = 0; j < that.maxGate; ++j) {
      for (let i = 0; i < that.nq; ++i) {
        if (that.circuit[i][j] == -1) { continue; }
        const currGate = that.obj[that.circuit[i][j]];
        switch (currGate.gid) {
          case X: case Y: case Z: case H: case S:
            that.singleGate(i, currGate.gid);
            break;
          case CX:
            console.log('CX: %d->%d', currGate.Gateinput[0], i);
            that.cx(currGate.Gateinput[0], i);
            break;
          case M:
            ++that.nmeasure;
            that.ismeasure[i] = 1;
            break;
          default: break;
        }
      }
    }
  }

  getProb() {
    const that = this;
    let zero = '';
    for (let i = 0; i < (1 << that.nq); ++i) { zero += '0'; }
    const prob = new Array(1 << that.nmeasure);
    for (let i = 0; i < (1 << that.nmeasure); ++i) { prob[i] = 0; }
    for (let id = 0; id < (1 << that.nq); ++id) {
      // |000> ...
      let idx = '';
      // id 的二进制字符串
      const pat = (zero + id.toString(2)).slice(-1 * that.nq);
      // console.log(pat);
      for (let i = 0; i < pat.length; ++i) {
        if (that.ismeasure[i] == 1) {
          idx += pat[i];
        }
      }
      // console.log(idx, parseInt(idx, 2));
      prob[parseInt(idx, 2)] += that.state.v[id].mod2();
    }
    return prob;
  }

  getQState() {
    const that = this;
    const qstate = Array();
    const wide = 1 << that.nmeasure;
    let zero = '';
    for (let i = 0; i < that.nmeasure; ++i) { zero += '0'; }
    for (let i = 0; i < wide; ++i) {
      qstate.push((zero + i.toString(2)).slice(-1 * that.nmeasure));
    }
    return qstate;
  }
}

export default QSimulator;
